﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="SeleniumTable.cs" company="">
// </copyright>
// <summary>
//   The selenium table.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace UITest.Selenium.SeleniumControls.HtmlControls
{
    using System;
    using System.Collections.ObjectModel;
    using System.Linq;

    using OpenQA.Selenium;

    using UITest.Selenium.SeleniumContext;

    /// <summary>
    ///     The selenium table search options.
    /// </summary>
    public enum SeleniumTableSearchOptions
    {
        /// <summary>
        ///     The normal.
        /// </summary>
        Normal, 

        /// <summary>
        ///     The normal tight.
        /// </summary>
        NormalTight, 

        /// <summary>
        ///     The greedy.
        /// </summary>
        Greedy, 

        /// <summary>
        ///     The starts with.
        /// </summary>
        StartsWith, 

        /// <summary>
        ///     The ends with.
        /// </summary>
        EndsWith
    }

    /// <summary>
    ///     The selenium table.
    /// </summary>
    public class SeleniumTable : SeleniumWebObject
    {
        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="SeleniumTable"/> class.
        /// </summary>
        /// <param name="seleniumContext">
        /// The selenium context.
        /// </param>
        /// <param name="findBy">
        /// The find by.
        /// </param>
        public SeleniumTable(SeleniumContext seleniumContext, By findBy)
            : base(seleniumContext, findBy)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SeleniumTable"/> class.
        /// </summary>
        /// <param name="seleniumContext">
        /// The selenium context.
        /// </param>
        /// <param name="webElement">
        /// The web element.
        /// </param>
        public SeleniumTable(SeleniumContext seleniumContext, IWebElement webElement)
            : base(seleniumContext, webElement)
        {
        }

        #endregion

        #region Public Properties

        /// <summary>
        ///     Gets the column count.
        /// </summary>
        public int ColumnCount
        {
            get
            {
                this.WaitForControlReady();
                return this.GetWebElement().FindElements(By.TagName("th")).Count;
            }
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// The click on cell.
        /// </summary>
        /// <param name="rowIndex">
        /// The row index.
        /// </param>
        /// <param name="columnIndex">
        /// The column index.
        /// </param>
        public void ClickOnCell(int rowIndex, int columnIndex)
        {
            // Find the cell
            IWebElement bodyElement = this.GetWebElement().FindElement(By.TagName("tbody"));
            ReadOnlyCollection<IWebElement> rowElements = bodyElement.FindElements(By.TagName("tr"));
            ReadOnlyCollection<IWebElement> colElements = rowElements[rowIndex].FindElements(By.TagName("td"));
            var element = new SeleniumWebObject(this.SeleniumContext, colElements[columnIndex]);
            element.Click();
        }

        /// <summary>
        /// The click on header cell.
        /// </summary>
        /// <param name="headerName">
        /// The header name.
        /// </param>
        public void ClickOnHeaderCell(string headerName)
        {
            // Find the cell
            IWebElement cellElement =
                this.GetWebElement().FindElements(By.TagName("th")).Select(x => x).First(x => x.Text == headerName);
            var seleniumHeaderCell = new SeleniumHeaderCell(this.SeleniumContext, cellElement);
            seleniumHeaderCell.Click();
        }

        /// <summary>
        /// The click on header cell.
        /// </summary>
        /// <param name="rowIndex">
        /// The row index.
        /// </param>
        /// <param name="columnIndex">
        /// The column index.
        /// </param>
        public void ClickOnHeaderCell(int rowIndex, int columnIndex)
        {
            // Find the cell
            ReadOnlyCollection<IWebElement> rowElements = this.GetWebElement().FindElements(By.TagName("tr"));
            ReadOnlyCollection<IWebElement> colElements = rowElements[rowIndex].FindElements(By.TagName("th"));
            var seleniumHeaderCell = new SeleniumHeaderCell(this.SeleniumContext, colElements[columnIndex]);
            seleniumHeaderCell.Click();
        }

        /// <summary>
        /// The double click on cell.
        /// </summary>
        /// <param name="rowIndex">
        /// The row index.
        /// </param>
        /// <param name="columnIndex">
        /// The column index.
        /// </param>
        public void DoubleClickOnCell(int rowIndex, int columnIndex)
        {
            // Find the cell
            IWebElement bodyElement = this.GetWebElement().FindElement(By.TagName("tbody"));
            ReadOnlyCollection<IWebElement> rowElements = bodyElement.FindElements(By.TagName("tr"));
            ReadOnlyCollection<IWebElement> colElements = rowElements[rowIndex].FindElements(By.TagName("td"));
            var element = new SeleniumWebObject(this.SeleniumContext, colElements[columnIndex]);
            element.DoubleClick();
        }

        /// <summary>
        /// The find row.
        /// </summary>
        /// <param name="headerName">
        /// The header name.
        /// </param>
        /// <param name="valueToSearch">
        /// The value to search.
        /// </param>
        /// <param name="option">
        /// The option.
        /// </param>
        /// <returns>
        /// The <see cref="int"/>.
        /// </returns>
        public int FindRow(string headerName, string valueToSearch, SeleniumTableSearchOptions option)
        {
            return this.FindRow(this.GetColumnIndex(headerName), valueToSearch, option);
        }

        /// <summary>
        /// The find row.
        /// </summary>
        /// <param name="columnIndex">
        /// The column index.
        /// </param>
        /// <param name="valueToSearch">
        /// The value to search.
        /// </param>
        /// <param name="option">
        /// The option.
        /// </param>
        /// <param name="startingRowIndex">
        /// The starting row index.
        /// </param>
        /// <returns>
        /// The <see cref="int"/>.
        /// </returns>
        public int FindRow(
            int columnIndex, 
            string valueToSearch, 
            SeleniumTableSearchOptions option, 
            int startingRowIndex = 0)
        {
            this.WaitForControlReady();
            int rowIndex = -1;
            

            // Search for Row contains the value to limit the collection
            ReadOnlyCollection<IWebElement> tempHtmlRowControls = this.GetWebElement().FindElements(By.TagName("tr"));

            for (int i = 0; i < tempHtmlRowControls.Count; i++)
            {
                if (tempHtmlRowControls[i].Text.Contains(valueToSearch))
                {
                    ReadOnlyCollection<IWebElement> htmlcellControls =
                        tempHtmlRowControls[i].FindElements(By.TagName("td"));

                    IWebElement htmlcellControl = htmlcellControls[columnIndex];

                    if (option == SeleniumTableSearchOptions.Normal)
                    {
                        if (htmlcellControl.Text.Equals(valueToSearch))
                        {
                            rowIndex = i;
                            if (rowIndex >= startingRowIndex)
                            {
                                return rowIndex;
                            }
                        }
                    }
                    else if (option == SeleniumTableSearchOptions.NormalTight)
                    {
                        if (htmlcellControl.Text.Trim().Equals(valueToSearch))
                        {
                            rowIndex = i;
                            if (rowIndex >= startingRowIndex)
                            {
                                return rowIndex;
                            }
                        }
                    }
                    else if (option == SeleniumTableSearchOptions.StartsWith)
                    {
                        if (htmlcellControl.Text.StartsWith(valueToSearch))
                        {
                            rowIndex = i;
                            if (rowIndex >= startingRowIndex)
                            {
                                return rowIndex;
                            }
                        }
                    }
                    else if (option == SeleniumTableSearchOptions.EndsWith)
                    {
                        if (htmlcellControl.Text.EndsWith(valueToSearch))
                        {
                            rowIndex = i;
                            if (rowIndex >= startingRowIndex)
                            {
                                return rowIndex;
                            }
                        }
                    }
                    else if (option == SeleniumTableSearchOptions.Greedy)
                    {
                        if (htmlcellControl.Text.IndexOf(valueToSearch) > -1)
                        {
                            rowIndex = i;
                            if (rowIndex >= startingRowIndex)
                            {
                                return rowIndex;
                            }
                        }
                    }
                }
            }

            return rowIndex;
        }

        /// <summary>
        /// The get cell.
        /// </summary>
        /// <param name="rowIndex">
        /// The row index.
        /// </param>
        /// <param name="colIndex">
        /// The col index.
        /// </param>
        /// <returns>
        /// The <see cref="SeleniumWebObject"/>.
        /// </returns>
        public SeleniumWebObject GetCell(int rowIndex, int colIndex)
        {
            // Find the cell
            IWebElement tableElement = this.FindElement();
            ReadOnlyCollection<IWebElement> rowElements = tableElement.FindElements(By.TagName("tr"));
            ReadOnlyCollection<IWebElement> colElements = rowElements[rowIndex].FindElements(By.TagName("td"));
            IWebElement cellElement = colElements[colIndex];

            return new SeleniumWebObject(this.SeleniumContext, cellElement);
        }

        /// <summary>
        /// The get cell child.
        /// </summary>
        /// <param name="rowIndex">
        /// The row index.
        /// </param>
        /// <param name="columnName">
        /// The column name.
        /// </param>
        /// <typeparam name="T2">
        /// </typeparam>
        /// <returns>
        /// The <see cref="T2"/>.
        /// </returns>
        public T2 GetCellChild<T2>(int rowIndex, string columnName) where T2 : SeleniumWebObject
        {
            return this.GetCellChild<T2>(rowIndex, columnName, null);
        }

        /// <summary>
        /// The get cell child.
        /// </summary>
        /// <param name="rowIndex">
        /// The row index.
        /// </param>
        /// <param name="columnName">
        /// The column name.
        /// </param>
        /// <param name="searchBy">
        /// The search by.
        /// </param>
        /// <typeparam name="T2">
        /// </typeparam>
        /// <returns>
        /// The <see cref="T2"/>.
        /// </returns>
        public T2 GetCellChild<T2>(int rowIndex, string columnName, By searchBy) where T2 : SeleniumWebObject
        {
            int colIndex = this.GetColumnIndex(columnName);
            var control = this.GetCellChild<T2>(rowIndex, colIndex, searchBy);
            return control;
        }

        /// <summary>
        /// The get cell child.
        /// </summary>
        /// <param name="rowIndex">
        /// The row index.
        /// </param>
        /// <param name="colIndex">
        /// The col index.
        /// </param>
        /// <typeparam name="T2">
        /// </typeparam>
        /// <returns>
        /// The <see cref="T2"/>.
        /// </returns>
        public T2 GetCellChild<T2>(int rowIndex, int colIndex) where T2 : SeleniumWebObject
        {
            return this.GetCellChild<T2>(rowIndex, colIndex, null);
        }

        /// <summary>
        /// The get cell child.
        /// </summary>
        /// <param name="rowIndex">
        /// The row index.
        /// </param>
        /// <param name="colIndex">
        /// The col index.
        /// </param>
        /// <param name="searchBy">
        /// The search by.
        /// </param>
        /// <typeparam name="T2">
        /// </typeparam>
        /// <returns>
        /// The <see cref="T2"/>.
        /// </returns>
        /// <exception cref="Exception">
        /// </exception>
        public T2 GetCellChild<T2>(int rowIndex, int colIndex, By searchBy) where T2 : SeleniumWebObject
        {
            // Find the cell
            IWebElement tableElement = this.FindElement();
            ReadOnlyCollection<IWebElement> rowElements = tableElement.FindElements(By.TagName("tr"));
            ReadOnlyCollection<IWebElement> colElements = rowElements[rowIndex].FindElements(By.TagName("td"));
            IWebElement cellElement = colElements[colIndex];

            SeleniumWebObject returnObject;

            if (searchBy != null)
            {
                returnObject = new SeleniumWebObject(this.SeleniumContext, cellElement.FindElement(searchBy));
            }
            else if (typeof(T2) == typeof(SeleniumEdit))
            {
                returnObject = new SeleniumEdit(this.SeleniumContext, cellElement.FindElement(By.TagName("Input")));
            }
            else if (typeof(T2) == typeof(SeleniumCheckBox))
            {
                returnObject = new SeleniumCheckBox(this.SeleniumContext, cellElement.FindElement(By.CssSelector("[type='checkbox']")));
            }
            else
            {
                throw new Exception(string.Format("CreateControls: '{0}' not supported", typeof(T2).Name));
            }

            if (returnObject != null)
            {
                return returnObject.ToDerived<T2>();
                
            }

            return null;
        }

        /// <summary>
        ///     The get column headers.
        /// </summary>
        /// <returns>
        ///     The <see cref="string[]" />.
        /// </returns>
        public string[] GetColumnHeaders()
        {
            string[] retArray;


           IWebElement rowElement = this.GetWebElement().FindElement(By.TagName("tr"));
            ReadOnlyCollection<IWebElement> headerElements = rowElement.FindElements(By.TagName("th"));

            if (headerElements.Count > 0)
            {
                retArray = new string[headerElements.Count];
                for (int i = 0; i < retArray.Length; i++)
                {
                    retArray[i] = headerElements[i].Text;
                }

                return retArray;
            }

            return null;
        }

        /// <summary>
        /// The get column index.
        /// </summary>
        /// <param name="columnName">
        /// The column name.
        /// </param>
        /// <returns>
        /// The <see cref="int"/>.
        /// </returns>
        public int GetColumnIndex(string columnName)
        {
            ReadOnlyCollection<IWebElement> rowElements = this.GetWebElement().FindElements(By.TagName("tr"));
            ReadOnlyCollection<IWebElement> headerElements = rowElements[0].FindElements(By.TagName("th"));

            int columnIndex = -1;

            columnIndex = headerElements.IndexOf(headerElements.Select(x => x).First(x => x.Text.Contains(columnName)));

            /*
            for (int i = 0; i < headerElements.Count; i++)
            {
                if (headerElements[i].Text.Contains(columnName))
                {
                    columnIndex = i;
                    return columnIndex;
                }
            }
             */

            return columnIndex;
        }

        public string[] GetRowHeaders()
        {
            string[] retArray;

            
            ReadOnlyCollection<IWebElement> rowElements = this.GetWebElement().FindElements(By.TagName("tr"));

            if (rowElements.Count > 0)
            {
                retArray = new string[rowElements.Count];
                for (int i = 0; i < retArray.Length; i++)
                {
                    retArray[i] = rowElements[i].FindElement(By.TagName("th")).Text;
                }

                return retArray;
            }

            return null;
        }

        public int RowCount
        {
            get
            {
                this.WaitForControlReady();
                return this.GetWebElement().FindElements(By.TagName("tr")).Count;
            }
        }


        #endregion
    }
}